import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig } from "axios";
// @ts-ignore
import crypoto from "./crypto";
import { message, notification } from "ant-design-vue";
import { useStore } from "@/store/index";
import XEUtils from "xe-utils";
import { isProdEnv } from "./index";

// @ts-ignore
const env = import.meta.env;

// @ts-ignore 服务请求地址
//env.VITE_SERVER_URL
//
const baseURL = env.VITE_SERVER_URL;
// 文件上传地址
const uploadURL = baseURL + "oss/upload/upload_blogs";
// 图片上传地址
const uploadPicURL = baseURL + "oss/upload_pic/upload_blogs";
const uploadPicNCRURL = baseURL + "oss/ncr/upload_pic/upload_blogs";

export { baseURL, uploadURL, uploadPicURL, uploadPicNCRURL, axios };

/**
 * 统一认证请求头，暴露提供文件上传使用
 */
export const authHeader = (noToken): any => {
  const header = {
    "Platform-Ids": 0,
    "Shop-Ids": 0,
    "Client-Type": "SHOP_CONSOLE",
    "Content-Type": "application/json",
  };
  const { accessToken: token, enterprise } = useStore();
  if (!noToken) {
    if (token) header["Authorization"] = "Bearer " + token;
    if (enterprise?.id) header["Shop-Ids"] = enterprise?.id;
  }
  return header;
};

export interface ResponseData<T = any> {
  code: string;
  msg: string;
  data: T;
}

/**
 * 自定义axios配置
 */
export interface AxiosRequestCustomConfig extends AxiosRequestConfig {
  /**
   * 是否加密，部分通过接口上传时会使用，默认值 true 仅生产环境时生效
   */
  isEncryption?: boolean;
  /**
   * 是否传token，部分接口不传token，默认是存有token就会传
   */
  noToken?: boolean;
}

/**
 * ArrayBuffer转Uint8Array解码器
 */
const textDecoder = new TextDecoder("UTF-8");

class HttpRequest {
  public request(config: AxiosRequestCustomConfig): Promise<ResponseData> {
    // 我们实际调用接口的时候调用实例的这个方法，他返回一个AxiosPromise
    // 这里使用axios.create方法创建一个axios实例，他是一个函数，同时这个函数包含多个属性
    const instance: AxiosInstance = axios.create({
      baseURL,
    });

    // 调用interceptors方法使拦截器生效
    this.interceptors(instance);

    return instance.request(config);
  }

  private interceptors(instance: AxiosInstance) {
    // 定义这个函数用于添加全局请求和响应拦截逻辑
    instance.interceptors.request.use(
      // @ts-ignore
      (config: AxiosRequestCustomConfig) => {
        const { data, isEncryption = true, noToken = false } = config;
        const headers = { ...config.headers, ...authHeader(noToken) };

        // 生产环境接口请求需要加密，localStorage.noEncrypt内有任何内容都不加密，用于线上参数查看
        if (
          false &&
          isEncryption &&
          !localStorage.hasOwnProperty("noEncrypt")
        ) {
          const timestamp = Date.now();
          headers[crypoto.EN_CH] = timestamp;

          // 请求体为JSON时加密请求参数，并设置请求体为纯文本
          if (data && XEUtils.isObject(data)) {
            headers["Content-Type"] = "text/plain";
            config.data = crypoto.encryptCBC(JSON.stringify(data), timestamp);
          }
        }

        config.headers = headers;

        return config;
      },
      (error) => {
        return Promise.reject(error);
      }
    );

    instance.interceptors.response.use(
      async (response) => {
        // res的类型是AxiosResponse<any>，包含六个字段，其中data是服务端返回的数据
        // eslint-disable-next-line prefer-const
        let { data, status, config, headers } = response;

        if (status === 200) {
          const contentType = headers["content-type"];

          // TODO 导出因后台未返回contentType，需要手动设置返回contentType为ArrayBuffer无法解析错误信息
          if (data instanceof Blob || data instanceof ArrayBuffer) {
            // 返回错误信息时contentType为application/json，需要将ArrayBuffer转换为JSON
            if (contentType && contentType.startsWith("application/json")) {
              if (data instanceof ArrayBuffer) {
                data = JSON.parse(textDecoder.decode(new Uint8Array(data)));
              } else if (data instanceof Blob) {
                data = JSON.parse(await data.text());
              }
            } else {
              // 下载返回流，直接返回data
              return data;
            }
          }

          // config.headers为请求头，加密返回内容类型为text/plain，判断加密请求头和返回响应类型
          if (
            config.headers.hasOwnProperty(crypoto.EN_CH) &&
            contentType === "text/plain"
          ) {
            const timestamp = config.headers[crypoto.EN_CH];
            try {
              data = JSON.parse(crypoto.decryptCBC(data, timestamp));
            } catch (e) {
              console.error(e);
              status = 500;
              data = {
                msg: "数据异常，请联系运维处理",
              };
            }
          }

          // 通常服务端会将响应状态码、提示信息、数据等放到返回的数据中
          const { code, msg } = data;

          if (code === 200) {
            return data;
          }

          message.error({
            key: status || 500,
            content: msg || "请刷新或清空浏览器缓存后重试",
          });
        } else if (status === 401) {
          useStore().lockVisible = true;
        }

        return Promise.reject(response);
      },
      (error) => {
        const { response } = error;

        if (response && response.status === 401) {
          useStore().lockVisible = true;
        } else {
          notification.error({
            key: error.name,
            message: "系统提示",
            description: error.message,
          });
        }
        return Promise.reject(error);
      }
    );
  }
}

export default new HttpRequest();
